import sys
import os
import time
import serial
import serial.tools.list_ports
import struct
import csv
from datetime import datetime, timedelta
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *


class SerialSender(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("数据记录仪工具 v2.0")
        self.setGeometry(100, 100, 1000, 700)

        # 初始化变量
        self.serial_ports = [None, None]
        self.send_thread = None
        self.sent_data_file = None
        self.total_packets = 0
        self.packet_counter = 0
        self.start_time = None
        self.last_update_time = None
        self.last_sent_bytes = 0
        self.current_speed = 0

        # 创建UI
        self.create_ui()

    def create_ui(self):
        main_widget = QWidget()
        main_layout = QVBoxLayout()

        # 串口配置区
        port_group = QGroupBox("串口配置")
        port_layout = QHBoxLayout()

        self.port_widgets = []
        for i in range(2):
            group = QGroupBox(f"串口 {i + 1}")
            layout = QVBoxLayout()

            # 端口选择
            port_layout_inner = QHBoxLayout()
            port_layout_inner.addWidget(QLabel("端口:"))
            self.port_cb = QComboBox()
            self.refresh_ports(self.port_cb)
            port_layout_inner.addWidget(self.port_cb)

            refresh_btn = QPushButton("刷新")
            refresh_btn.clicked.connect(lambda _, cb=self.port_cb: self.refresh_ports(cb))
            port_layout_inner.addWidget(refresh_btn)
            layout.addLayout(port_layout_inner)

            # 波特率选择
            baud_layout = QHBoxLayout()
            baud_layout.addWidget(QLabel("波特率:"))
            baud_cb = QComboBox()
            baud_cb.addItems(["9600", "19200", "38400", "57600", "115200", "230400", "460800", "921600"])
            baud_cb.setCurrentText("115200")
            baud_layout.addWidget(baud_cb)
            layout.addLayout(baud_layout)

            # 打开/关闭按钮
            self.toggle_btn = QPushButton("打开端口")
            self.toggle_btn.setCheckable(True)
            layout.addWidget(self.toggle_btn)

            group.setLayout(layout)
            port_layout.addWidget(group)
            self.port_widgets.append({
                "group": group,
                "port_cb": self.port_cb,
                "baud_cb": baud_cb,
                "toggle_btn": self.toggle_btn
            })

        port_group.setLayout(port_layout)
        main_layout.addWidget(port_group)

        # 数据配置区
        data_group = QGroupBox("数据配置")
        data_layout = QFormLayout()

        # 数据内容
        self.data_te = QTextEdit()
        self.data_te.setPlaceholderText("输入要发送的数据，支持HEX格式（以空格分隔）如：01 02 AB CD")
        self.data_te.setMinimumHeight(60)
        data_layout.addRow("数据内容:", self.data_te)

        # 发送选项
        options_layout = QHBoxLayout()

        # 数据格式
        format_layout = QHBoxLayout()
        format_layout.addWidget(QLabel("数据格式:"))
        self.format_cb = QComboBox()
        self.format_cb.addItems(["自动检测", "文本", "HEX"])
        format_layout.addWidget(self.format_cb)
        options_layout.addLayout(format_layout)

        # 数据包大小
        size_layout = QHBoxLayout()
        size_layout.addWidget(QLabel("包大小(字节):"))
        self.packet_size = QSpinBox()
        self.packet_size.setRange(1, 1024)
        self.packet_size.setValue(32)
        size_layout.addWidget(self.packet_size)
        options_layout.addLayout(size_layout)

        # 发送间隔
        interval_layout = QHBoxLayout()
        interval_layout.addWidget(QLabel("间隔(ms):"))
        self.interval_sp = QSpinBox()
        self.interval_sp.setRange(1, 10000)
        self.interval_sp.setValue(500)
        interval_layout.addWidget(self.interval_sp)
        options_layout.addLayout(interval_layout)

        data_layout.addRow(options_layout)

        # 总发送量
        total_layout = QHBoxLayout()
        total_layout.addWidget(QLabel("总发送量:"))

        self.total_size = QSpinBox()
        self.total_size.setRange(1, 1000000)
        self.total_size.setValue(1000)
        total_layout.addWidget(self.total_size)

        # 添加单位选择
        self.unit_cb = QComboBox()
        self.unit_cb.addItems(["B", "KB", "MB"])
        self.unit_cb.setCurrentText("KB")
        total_layout.addWidget(self.unit_cb)

        data_layout.addRow(total_layout)

        # 文件保存
        file_layout = QHBoxLayout()
        file_layout.addWidget(QLabel("保存文件:"))
        self.filename_le = QLineEdit(f"serial_data_{datetime.now().strftime('%Y%m%d_%H%M%S')}.bin")
        file_layout.addWidget(self.filename_le)
        browse_btn = QPushButton("浏览...")
        browse_btn.clicked.connect(self.select_file)
        file_layout.addWidget(browse_btn)
        data_layout.addRow(file_layout)

        data_group.setLayout(data_layout)
        main_layout.addWidget(data_group)

        # 控制区
        control_layout = QHBoxLayout()

        self.start_btn = QPushButton("开始发送")
        self.start_btn.setStyleSheet("background-color: green; color: white;")
        self.start_btn.clicked.connect(self.start_sending)
        control_layout.addWidget(self.start_btn)

        self.stop_btn = QPushButton("停止发送")
        self.stop_btn.setStyleSheet("background-color: red; color: white;")
        self.stop_btn.setEnabled(False)
        self.stop_btn.clicked.connect(self.stop_sending)
        control_layout.addWidget(self.stop_btn)

        main_layout.addLayout(control_layout)

        # 进度区
        progress_group = QGroupBox("发送进度")
        progress_layout = QVBoxLayout()

        # 进度条
        self.progress_bar = QProgressBar()
        self.progress_bar.setRange(0, 100)
        self.progress_bar.setTextVisible(True)
        progress_layout.addWidget(self.progress_bar)

        # 状态信息
        status_layout = QGridLayout()

        status_layout.addWidget(QLabel("已发送:"), 0, 0)
        self.sent_lbl = QLabel("0 字节")
        status_layout.addWidget(self.sent_lbl, 0, 1)

        status_layout.addWidget(QLabel("剩余:"), 0, 2)
        self.remaining_lbl = QLabel("0 字节")
        status_layout.addWidget(self.remaining_lbl, 0, 3)

        status_layout.addWidget(QLabel("进度:"), 1, 0)
        self.percentage_lbl = QLabel("0%")
        status_layout.addWidget(self.percentage_lbl, 1, 1)

        status_layout.addWidget(QLabel("速度:"), 1, 2)
        self.speed_lbl = QLabel("0 KB/s")
        status_layout.addWidget(self.speed_lbl, 1, 3)

        # 新增发送频率显示
        status_layout.addWidget(QLabel("发送频率:"), 2, 0)
        self.frequency_lbl = QLabel("0 包/秒")
        status_layout.addWidget(self.frequency_lbl, 2, 1)

        status_layout.addWidget(QLabel("已用时间:"), 2, 2)
        self.elapsed_lbl = QLabel("00:00:00")
        status_layout.addWidget(self.elapsed_lbl, 2, 3)

        status_layout.addWidget(QLabel("剩余时间:"), 3, 0)
        self.eta_lbl = QLabel("00:00:00")
        status_layout.addWidget(self.eta_lbl, 3, 1)

        progress_layout.addLayout(status_layout)
        progress_group.setLayout(progress_layout)
        main_layout.addWidget(progress_group)

        # 日志区
        log_group = QGroupBox("操作日志")
        log_layout = QVBoxLayout()
        self.log_te = QTextEdit()
        self.log_te.setReadOnly(True)
        log_layout.addWidget(self.log_te)
        log_group.setLayout(log_layout)
        main_layout.addWidget(log_group)

        # 比较工具按钮
        compare_btn = QPushButton("打开比较工具")
        compare_btn.clicked.connect(self.open_comparator)
        main_layout.addWidget(compare_btn)

        # CAN转换工具按钮
        can_btn = QPushButton("打开CAN转换工具")
        can_btn.clicked.connect(self.open_can_converter)
        main_layout.addWidget(can_btn)

        main_widget.setLayout(main_layout)
        self.setCentralWidget(main_widget)

        # 状态栏
        self.statusBar().showMessage("就绪")

        # 定时更新UI
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.update_progress)
        self.timer.start(200)

    def refresh_ports(self, combo_box):
        combo_box.clear()
        ports = serial.tools.list_ports.comports()
        for port in ports:
            combo_box.addItem(port.device, port.description)

    def select_file(self):
        filename, _ = QFileDialog.getSaveFileName(
            self, "保存发送数据",
            self.filename_le.text(),
            "Binary Files (*.bin);;All Files (*)"
        )
        if filename:
            self.filename_le.setText(filename)

    def open_comparator(self):
        self.comparator = FileComparator()
        self.comparator.show()

    def open_can_converter(self):
        self.can_converter = CANConverter()
        self.can_converter.show()

    def start_sending(self):
        # 检查端口
        ports_open = [w['toggle_btn'].isChecked() for w in self.port_widgets]
        if not any(ports_open):
            self.log("错误：请先打开至少一个串口")
            return

        # 准备数据
        raw_text = self.data_te.toPlainText().strip()
        if not raw_text:
            self.log("错误：请输入要发送的数据")
            return

        data_format = self.format_cb.currentText()
        if data_format == "自动检测":
            if any(char not in "0123456789ABCDEFabcdef " for char in raw_text):
                data_bytes = raw_text.encode()
            else:
                data_bytes = self.hex_to_bytes(raw_text)
        elif data_format == "HEX":
            data_bytes = self.hex_to_bytes(raw_text)
        else:
            data_bytes = raw_text.encode()

        if not data_bytes:
            self.log("错误：无法解析数据内容")
            return

        # 准备文件
        try:
            self.sent_data_file = open(self.filename_le.text(), 'wb')
        except Exception as e:
            self.log(f"文件错误：{str(e)}")
            return

        # 计算包数
        unit = self.unit_cb.currentText()
        if unit == "KB":
            total_bytes = self.total_size.value() * 1024
        elif unit == "MB":
            total_bytes = self.total_size.value() * 1024 * 1024
        else:  # B
            total_bytes = self.total_size.value()

        packet_size = self.packet_size.value()
        self.total_packets = total_bytes // packet_size
        self.packet_counter = 0
        self.start_time = time.time()
        self.last_update_time = time.time()
        self.last_sent_bytes = 0
        self.current_speed = 0

        # 创建发送线程
        self.send_thread = SendThread(
            [{
                'port': w['port_cb'].currentText(),
                'baud': int(w['baud_cb'].currentText()),
                'open': w['toggle_btn'].isChecked()
            } for w in self.port_widgets],
            data_bytes,
            packet_size,
            total_bytes,
            self.interval_sp.value() / 1000.0,
            self.sent_data_file
        )

        self.send_thread.update_progress.connect(self.update_counters)
        self.send_thread.finished.connect(self.sending_completed)
        self.send_thread.log_signal.connect(self.log)

        # 更新UI状态
        self.start_btn.setEnabled(False)
        self.stop_btn.setEnabled(True)
        self.progress_bar.setValue(0)

        # 开始发送
        self.send_thread.start()
        self.log("开始发送数据...")
        self.log(f"总发送量: {self.total_size.value()} {unit} ({total_bytes} 字节)")
        self.log(f"包大小: {packet_size} 字节, 总包数: {self.total_packets}")

    def stop_sending(self):
        if self.send_thread and self.send_thread.isRunning():
            self.send_thread.stop()
            self.log("发送停止请求已发送，等待完成...")

    def sending_completed(self):
        # 关闭文件
        if self.sent_data_file:
            self.sent_data_file.close()
            self.sent_data_file = None

        # 更新UI
        self.start_btn.setEnabled(True)
        self.stop_btn.setEnabled(False)

        self.log("数据发送完成")

    def update_counters(self, sent_bytes, elapsed):
        current_time = time.time()
        time_diff = current_time - self.last_update_time

        if time_diff > 0.2:  # 每200ms更新一次频率
            bytes_diff = sent_bytes - self.last_sent_bytes
            self.current_speed = bytes_diff / time_diff / 1024  # KB/s
            packets_diff = bytes_diff / self.packet_size.value()
            self.frequency_lbl.setText(f"{packets_diff / time_diff:.1f} 包/秒")
            self.last_sent_bytes = sent_bytes
            self.last_update_time = current_time

        self.packet_counter = sent_bytes / self.packet_size.value()

    def update_progress(self):
        if not self.start_time or not self.total_packets:
            return

        elapsed = time.time() - self.start_time
        self.elapsed_lbl.setText(str(timedelta(seconds=int(elapsed)))[:8])

        # 更新进度
        if self.total_packets > 0:
            progress = min(100, int(self.packet_counter * 100 / self.total_packets))
            self.progress_bar.setValue(progress)
            self.percentage_lbl.setText(f"{progress}%")

            # 计算速度
            if elapsed > 0:
                speed = self.packet_counter * self.packet_size.value() / elapsed / 1024
                self.speed_lbl.setText(f"{speed:.1f} KB/s")
            else:
                self.speed_lbl.setText("0 KB/s")

            # 计算剩余时间
            remaining_bytes = (self.total_packets - self.packet_counter) * self.packet_size.value()
            if self.packet_counter > 0 and elapsed > 0:
                eta = (remaining_bytes / (self.packet_counter * self.packet_size.value() / elapsed))
                self.eta_lbl.setText(str(timedelta(seconds=int(eta)))[:8])
            else:
                self.eta_lbl.setText("--:--:--")

            # 更新字节计数
            sent_bytes = self.packet_counter * self.packet_size.value()
            self.sent_lbl.setText(f"{sent_bytes:,} 字节")
            self.remaining_lbl.setText(f"{remaining_bytes:,} 字节")

    def log(self, message):
        timestamp = datetime.now().strftime("%H:%M:%S")
        self.log_te.append(f"[{timestamp}] {message}")
        self.statusBar().showMessage(message)
        QApplication.processEvents()

    def hex_to_bytes(self, hex_str):
        try:
            return bytes.fromhex(''.join(hex_str.split()))
        except ValueError:
            return None

    def closeEvent(self, event):
        if self.send_thread and self.send_thread.isRunning():
            self.send_thread.stop()
            self.send_thread.wait(2000)

        if self.sent_data_file:
            self.sent_data_file.close()

        event.accept()


class SendThread(QThread):
    update_progress = pyqtSignal(int, float)
    finished = pyqtSignal()
    log_signal = pyqtSignal(str)

    def __init__(self, ports_config, data_pattern, packet_size, total_bytes, interval, save_file):
        super().__init__()
        self.ports_config = ports_config
        self.data_pattern = data_pattern
        self.packet_size = packet_size
        self.total_bytes = total_bytes
        self.interval = interval
        self.save_file = save_file
        self.ser_ports = []
        self.running = False
        self.sent_bytes = 0
        self.start_time = time.time()

    def run(self):
        self.running = True

        # 打开串口
        for i, config in enumerate(self.ports_config):
            if config['open']:
                try:
                    ser = serial.Serial(
                        port=config['port'],
                        baudrate=config['baud'],
                        bytesize=serial.EIGHTBITS,
                        parity=serial.PARITY_NONE,
                        stopbits=serial.STOPBITS_ONE,
                        timeout=0.1
                    )
                    self.ser_ports.append(ser)
                    self.log_signal.emit(f"串口 {config['port']} 已打开")
                except Exception as e:
                    self.log_signal.emit(f"打开串口 {config['port']} 失败: {str(e)}")
            else:
                self.ser_ports.append(None)

        # 数据包模板
        packet_template = self.data_pattern * (self.packet_size // len(self.data_pattern) + 1)
        packet_template = packet_template[:self.packet_size]

        # 主发送循环
        packet_counter = 0
        while self.running and self.sent_bytes < self.total_bytes:
            # 生成带校验和的数据包
            packet = packet_template + self.calculate_checksum(packet_template)

            # 保存数据到文件 - 与实际发送的数据保持一致
            if self.save_file:
                self.save_file.write(packet)
                self.save_file.flush()
                os.fsync(self.save_file.fileno())

            # 发送到所有打开的串口
            for ser in self.ser_ports:
                if ser is not None:
                    try:
                        ser.write(packet)
                    except Exception as e:
                        self.log_signal.emit(f"发送错误: {str(e)}")
                        ser.close()
                        ser = None

            # 更新状态
            self.sent_bytes += self.packet_size
            packet_counter += 1  # 增加包计数器

            # 间隔延时
            if self.interval > 0:
                time.sleep(self.interval)

        # 关闭串口
        for ser in self.ser_ports:
            if ser is not None and ser.is_open:
                ser.close()

        self.finished.emit()

    def stop(self):
        self.running = False

    def calculate_checksum(self, data):
        """计算简单的校验和"""
        return sum(data).to_bytes(1, 'little')

class FileComparator(QDialog):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("文件比较工具")
        self.setGeometry(200, 200, 600, 400)

        layout = QVBoxLayout()

        # 文件选择
        file_layout = QHBoxLayout()
        file_layout.addWidget(QLabel("发送文件:"))
        self.file1_le = QLineEdit()
        file_layout.addWidget(self.file1_le)
        browse1_btn = QPushButton("浏览...")
        browse1_btn.clicked.connect(lambda: self.select_file(self.file1_le))
        file_layout.addWidget(browse1_btn)
        layout.addLayout(file_layout)

        file_layout2 = QHBoxLayout()
        file_layout2.addWidget(QLabel("接收文件:"))
        self.file2_le = QLineEdit()
        file_layout2.addWidget(self.file2_le)
        browse2_btn = QPushButton("浏览...")
        browse2_btn.clicked.connect(lambda: self.select_file(self.file2_le))
        file_layout2.addWidget(browse2_btn)
        layout.addLayout(file_layout2)

        # 比较按钮
        compare_btn = QPushButton("比较文件")
        compare_btn.clicked.connect(self.compare_files)
        layout.addWidget(compare_btn)

        # 结果显示
        result_group = QGroupBox("比较结果")
        result_layout = QVBoxLayout()
        self.result_te = QTextEdit()
        self.result_te.setReadOnly(True)
        result_layout.addWidget(self.result_te)
        result_group.setLayout(result_layout)
        layout.addWidget(result_group)

        self.setLayout(layout)

    def select_file(self, line_edit):
        filename, _ = QFileDialog.getOpenFileName(self, "选择文件")
        if filename:
            line_edit.setText(filename)

    def compare_files(self):
        file1 = self.file1_le.text()
        file2 = self.file2_le.text()

        if not file1 or not file2:
            self.result_te.setText("请选择两个要比较的文件")
            return

        try:
            with open(file1, 'rb') as f1, open(file2, 'rb') as f2:
                data1 = f1.read()
                data2 = f2.read()

                size1 = len(data1)
                size2 = len(data2)

                # 比较大小
                if size1 != size2:
                    self.result_te.append(f"文件大小不同: {size1} 字节 vs {size2} 字节")

                # 比较内容
                min_size = min(size1, size2)
                block_size = 1024  # 1KB数据块
                errors = 0
                error_positions = []

                for block_start in range(0, min_size, block_size):
                    block_end = min(block_start + block_size, min_size)
                    block1 = data1[block_start:block_end]
                    block2 = data2[block_start:block_end]

                    if block1 != block2:
                        # 在块内定位具体错误位置
                        for i in range(len(block1)):
                            pos = block_start + i
                            if block1[i] != block2[i]:
                                errors += 1
                                error_positions.append(pos)
                                # if errors >= 1000:  # 最多记录1000个错误
                                #     break
                        if errors >= 1000:
                            break

                # 输出结果
                self.result_te.clear()
                self.result_te.append(f"=== 文件比较结果 ===")
                self.result_te.append(f"发送文件: {file1} ({size1} 字节)")
                self.result_te.append(f"接收文件: {file2} ({size2} 字节)")
                self.result_te.append(f"共同长度: {min_size} 字节")
                self.result_te.append(f"发现错误: {errors} 处")
                self.result_te.append("")

                if errors > 0:
                    self.result_te.append("错误位置:")
                    for i, pos in enumerate(error_positions):
                        self.result_te.append(f"位置 {pos}: 0x{data1[pos]:02X} (发送) vs 0x{data2[pos]:02X} (接收)")
        except Exception as e:
            self.result_te.setText(f"比较文件时出错: {str(e)}")


class CANConverter(QDialog):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("CAN数据转换工具")
        self.setGeometry(200, 200, 600, 500)

        layout = QVBoxLayout()

        # 文件选择
        file_layout = QHBoxLayout()
        file_layout.addWidget(QLabel("输入文件(.dat):"))
        self.input_le = QLineEdit()
        file_layout.addWidget(self.input_le)
        input_btn = QPushButton("浏览...")
        input_btn.clicked.connect(self.select_input_file)
        file_layout.addWidget(input_btn)
        layout.addLayout(file_layout)

        file_layout2 = QHBoxLayout()
        file_layout2.addWidget(QLabel("输出文件(.csv):"))
        self.output_le = QLineEdit()
        file_layout2.addWidget(self.output_le)
        output_btn = QPushButton("浏览...")
        output_btn.clicked.connect(self.select_output_file)
        file_layout2.addWidget(output_btn)
        layout.addLayout(file_layout2)

        # 进度条
        self.progress_bar = QProgressBar()
        self.progress_bar.setRange(0, 100)
        layout.addWidget(self.progress_bar)

        # 状态标签
        self.status_label = QLabel("准备就绪")
        layout.addWidget(self.status_label)

        # 转换按钮
        convert_btn = QPushButton("开始转换")
        convert_btn.clicked.connect(self.start_conversion)
        layout.addWidget(convert_btn)

        # 结果文本框
        self.result_te = QTextEdit()
        self.result_te.setReadOnly(True)
        layout.addWidget(self.result_te)

        # 说明
        info_group = QGroupBox("转换说明")
        info_layout = QVBoxLayout()
        info_text = """1. 输入文件必须是二进制CAN日志(.dat)
2. 输出格式: 带0x前缀的16进制帧ID
3. 时间戳转换为: 秒.毫秒.微秒格式
4. 标准帧显示为0xXXXX格式，扩展帧为0xXXXXXXXX
5. 时间标识格式: 秒.毫秒.微秒 (如 123.456.789)"""
        info_label = QLabel(info_text)
        info_layout.addWidget(info_label)
        info_group.setLayout(info_layout)
        layout.addWidget(info_group)

        self.setLayout(layout)

    def select_input_file(self):
        filename, _ = QFileDialog.getOpenFileName(
            self, "选择CAN日志文件",
            "", "CAN日志文件 (*.dat);;所有文件 (*.*)"
        )
        if filename:
            self.input_le.setText(filename)

    def select_output_file(self):
        default_name = "converted_output.csv"
        filename, _ = QFileDialog.getSaveFileName(
            self, "保存CSV文件",
            default_name, "CSV文件 (*.csv)"
        )
        if filename:
            self.output_le.setText(filename)

    def start_conversion(self):
        input_file = self.input_le.text()
        output_file = self.output_le.text()

        if not input_file or not output_file:
            self.result_te.setText("请选择输入和输出文件")
            return

        self.progress_bar.setValue(0)
        self.status_label.setText("开始转换...")
        QApplication.processEvents()

        # 在后台线程中执行转换
        self.converter = CANConverterThread(input_file, output_file)
        self.converter.progress_updated.connect(self.update_progress)
        self.converter.finished.connect(self.conversion_complete)
        self.converter.error_occurred.connect(self.show_error)
        self.converter.start()

    def update_progress(self, value, message):
        self.progress_bar.setValue(value)
        self.status_label.setText(message)

    def conversion_complete(self, message):
        self.status_label.setText("转换完成!")
        self.result_te.append(message)
        QMessageBox.information(self, "成功", "文件转换完成!")

    def show_error(self, message):
        self.status_label.setText("转换错误")
        self.result_te.append(f"错误: {message}")
        QMessageBox.critical(self, "错误", message)


class CANConverterThread(QThread):
    progress_updated = pyqtSignal(int, str)
    finished = pyqtSignal(str)
    error_occurred = pyqtSignal(str)

    def __init__(self, input_path, output_path):
        super().__init__()
        self.input_path = input_path
        self.output_path = output_path

    def run(self):
        try:
            # 验证文件大小是否为17字节的倍数
            file_size = os.path.getsize(self.input_path)
            if file_size % 17 != 0:
                error_msg = f"文件大小不匹配: {file_size}字节\n必须是17字节(CAN_LogEntry大小)的倍数"
                self.error_occurred.emit(error_msg)
                return

            struct_format = '<IIB8B'  # 小端字节序
            entry_size = struct.calcsize(struct_format)
            total_entries = file_size // entry_size
            processed = 0

            with open(self.input_path, 'rb') as bin_file, open(self.output_path, 'w', newline='') as csv_file:
                csv_writer = csv.writer(csv_file)
                # 写入CSV表头
                csv_writer.writerow(
                    ['序号', '时间标识', '传输方向', '帧类型', '帧格式', '帧ID', '数据长度', '帧数据', '时间戳'])

                index = 1
                while True:
                    chunk = bin_file.read(entry_size)
                    if not chunk:
                        break

                    # 解析二进制数据
                    timestamp, can_id, dlc, *data = struct.unpack(struct_format, chunk)

                    # 时间格式化 (秒.毫秒.微秒)
                    total_seconds = timestamp / 1000.0  # 转换为秒（浮点数）
                    seconds = int(total_seconds)  # 获取整数秒部分
                    fractional = total_seconds - seconds  # 获取小数部分

                    milliseconds = int(fractional * 1000)  # 转换为毫秒(0-999)
                    microseconds = int((fractional * 1000000) % 1000)  # 获取微秒部分(0-999)

                    time_str = f"{seconds}.{milliseconds:03d}.{microseconds:03d}"

                    # 帧类型判断
                    frame_type = "标准帧" if can_id <= 0x7FF else "扩展帧"

                    # 帧ID格式化 (带0x前缀)
                    frame_id = f"0x{can_id:04X}" if frame_type == "标准帧" else f"0x{can_id:08X}"

                    # 数据域格式化 (HEX空格分隔)
                    hex_data = ' '.join(f"{b:02X}" for b in data[:dlc])

                    # 写入CSV行
                    csv_writer.writerow([
                        index,
                        time_str,
                        "发送",
                        frame_type,
                        "数据",
                        frame_id,
                        dlc,
                        hex_data,
                        timestamp
                    ])
                    index += 1
                    processed += 1

                    # 更新进度
                    if processed % 100 == 0 or processed == total_entries:
                        progress = int((processed / total_entries) * 100)
                        self.progress_updated.emit(progress, f"处理中: {processed}/{total_entries} 条记录")

            self.finished.emit(f"转换完成! 共处理 {processed} 条记录\n输出文件: {self.output_path}")

        except Exception as e:
            self.error_occurred.emit(f"转换过程中发生错误: {str(e)}")


if __name__ == "__main__":
    app = QApplication(sys.argv)
    app.setStyle("Fusion")

    # 设置更现代的UI样式
    palette = app.palette()
    palette.setColor(palette.Window, QColor(240, 240, 240))
    palette.setColor(palette.WindowText, QColor(0, 0, 0))
    palette.setColor(palette.Base, QColor(255, 255, 255))
    palette.setColor(palette.AlternateBase, QColor(240, 240, 240))
    palette.setColor(palette.Highlight, QColor(100, 149, 237))
    palette.setColor(palette.HighlightedText, QColor(255, 255, 255))
    app.setPalette(palette)

    window = SerialSender()
    window.show()
    sys.exit(app.exec_())